<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>函数参数翻转 - 表单验证系统</title>
    <link rel="stylesheet"
        href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/atom-one-dark.min.css">
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
            font-family: 'Arial', sans-serif;
        }

        body {
            background-color: #f5f7fa;
            padding: 20px;
            color: #333;
        }

        .container {
            max-width: 1000px;
            margin: 0 auto;
            background-color: #fff;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            padding: 30px;
        }

        h1 {
            text-align: center;
            margin-bottom: 30px;
            color: #2c3e50;
        }

        .description {
            background-color: #f8f9fa;
            padding: 15px;
            border-radius: 6px;
            margin-bottom: 30px;
            border-left: 4px solid #3498db;
        }

        /* 添加tabs的样式 */
        .tabs {
            display: flex;
            border-bottom: 1px solid #ddd;
            margin-bottom: 20px;
        }

        .tab {
            padding: 10px 20px;
            cursor: pointer;
            border: 1px solid transparent;
            border-bottom: none;
            margin-right: 5px;
            border-radius: 4px 4px 0 0;
            background-color: #f8f9fa;
            transition: all 0.3s ease;
        }

        .tab:hover {
            background-color: #e9ecef;
        }

        .tab.active {
            background-color: #fff;
            border-color: #ddd;
            border-bottom-color: #fff;
            margin-bottom: -1px;
            color: #3498db;
            font-weight: bold;
        }

        .tab-content {
            display: none;
        }

        .tab-content.active {
            display: block;
        }

        .form-group {
            margin-bottom: 20px;
        }

        label {
            display: block;
            margin-bottom: 8px;
            font-weight: bold;
        }

        input,
        select {
            width: 100%;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 16px;
        }

        button {
            background-color: #3498db;
            color: white;
            border: none;
            padding: 12px 20px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            transition: background-color 0.3s;
        }

        button:hover {
            background-color: #2980b9;
        }

        .validation-rules {
            margin-top: 30px;
            background-color: #f8f9fa;
            padding: 20px;
            border-radius: 6px;
        }

        .rule-item {
            margin-bottom: 10px;
            padding: 10px;
            background-color: #fff;
            border-radius: 4px;
            box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .rule-item button {
            padding: 6px 12px;
            font-size: 14px;
            background-color: #e74c3c;
        }

        .rule-item button:hover {
            background-color: #c0392b;
        }

        .result-panel {
            margin-top: 30px;
            padding: 20px;
            border-radius: 6px;
            background-color: #f8f9fa;
        }

        .result-item {
            padding: 10px;
            margin-bottom: 10px;
            border-radius: 4px;
        }

        .result-item.success {
            background-color: #d4edda;
            color: #155724;
        }

        .result-item.error {
            background-color: #f8d7da;
            color: #721c24;
        }

        .code-block {
            background-color: #282c34;
            padding: 15px;
            border-radius: 6px;
            margin: 20px 0;
            font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
            white-space: pre-wrap;
            border-left: 4px solid #3498db;
            color: #abb2bf;
            position: relative;
            overflow: auto;
        }

        .code-block code {
            font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
            font-size: 14px;
            line-height: 1.5;
        }

        .hljs {
            background: transparent;
            padding: 0;
        }
    </style>
</head>

<body>
    <div class="container">
        <h1>函数参数翻转 - 表单验证系统</h1>

        <div class="description">
            <p>这个系统展示了如何使用 <code>flip</code> 函数来创建灵活的表单验证系统。通过翻转函数参数顺序，我们可以更方便地组合和复用验证规则。</p>
        </div>

        <div class="tabs">
            <div class="tab active" data-tab="demo">演示</div>
            <div class="tab" data-tab="code">代码实现</div>
            <div class="tab" data-tab="explanation">原理解释</div>
        </div>

        <div class="tab-content active" id="demo">
            <div class="form-group">
                <label for="username">用户名</label>
                <input type="text" id="username" placeholder="请输入用户名">
            </div>

            <div class="form-group">
                <label for="email">邮箱</label>
                <input type="email" id="email" placeholder="请输入邮箱">
            </div>

            <div class="form-group">
                <label for="password">密码</label>
                <input type="password" id="password" placeholder="请输入密码">
            </div>

            <div class="form-group">
                <label for="age">年龄</label>
                <input type="number" id="age" placeholder="请输入年龄">
            </div>

            <div class="form-group">
                <label for="role">角色</label>
                <select id="role">
                    <option value="">请选择角色</option>
                    <option value="admin">管理员</option>
                    <option value="user">普通用户</option>
                    <option value="guest">访客</option>
                </select>
            </div>

            <button id="validate-btn">验证表单</button>

            <div class="validation-rules">
                <h3>当前验证规则</h3>
                <div id="rules-list"></div>
                <div class="form-group" style="margin-top: 20px;">
                    <label for="rule-field">添加验证规则</label>
                    <div style="display: flex; gap: 10px;">
                        <select id="rule-field" style="flex: 1;">
                            <option value="username">用户名</option>
                            <option value="email">邮箱</option>
                            <option value="password">密码</option>
                            <option value="age">年龄</option>
                            <option value="role">角色</option>
                        </select>
                        <select id="rule-type" style="flex: 1;">
                            <option value="required">必填</option>
                            <option value="minLength">最小长度</option>
                            <option value="maxLength">最大长度</option>
                            <option value="email">邮箱格式</option>
                            <option value="numeric">数字</option>
                            <option value="min">最小值</option>
                            <option value="max">最大值</option>
                            <option value="pattern">正则匹配</option>
                            <option value="oneOf">枚举值</option>
                        </select>
                        <input type="text" id="rule-value" placeholder="规则参数" style="flex: 1;">
                        <button id="add-rule-btn" style="width: auto;">添加规则</button>
                    </div>
                </div>
            </div>

            <div class="result-panel">
                <h3>验证结果</h3>
                <div id="validation-results"></div>
            </div>
        </div>

        <div class="tab-content" id="code">
            <h3>flip 函数实现</h3>
            <div class="code-block">
                <pre><code class="language-javascript">const flip =
    handler =>
        (first, ...rest) =>
            handler(...rest, first);</code></pre>
            </div>

            <h3>验证系统核心代码</h3>
            <div class="code-block">
                <pre><code class="language-javascript">// 基础验证函数 - 接收 (value, rule) 作为参数
const validators = {
    required: (value, rule) => value !== undefined && value !== null && value !== '',
    minLength: (value, rule) => value.length >= rule,
    maxLength: (value, rule) => value.length <= rule,
    email: (value, rule) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
    numeric: (value, rule) => !isNaN(parseFloat(value)) && isFinite(value),
    min: (value, rule) => parseFloat(value) >= rule,
    max: (value, rule) => parseFloat(value) <= rule,
    pattern: (value, rule) => new RegExp(rule).test(value),
    oneOf: (value, rule) => rule.split(',').includes(value)
};

// 使用 flip 翻转参数顺序，创建 (rule, value) 顺序的验证函数
const flippedValidators = {};
for (const key in validators) {
    flippedValidators[key] = flip(validators[key]);
}

// 创建字段验证函数 - 先接收规则，再接收值
const createFieldValidator = (field, type, param) => {
    const validator = flippedValidators[type];
    if (!validator) return null;

    // 创建一个已经绑定了规则参数的验证函数
    const boundValidator = validator.bind(null, param);

    // 返回一个接收字段值的函数
    return (formData) => {
        const result = boundValidator(formData[field]);
        return {
            field,
            type,
            param,
            value: formData[field],
            valid: result,
            message: result ?
                `${field} 验证通过` :
                getErrorMessage(field, type, param)
        };
    };
};

// 组合多个验证规则
const validateForm = (validators, formData) => {
    return validators.map(validator => validator(formData));
};</code></pre>
            </div>
        </div>

        <div class="tab-content" id="explanation">
            <h3>flip 函数原理</h3>
            <p>flip 函数是一个高阶函数，它接收一个函数作为参数，并返回一个新函数，这个新函数会将第一个参数放到最后，其余参数前移。</p>

            <div class="code-block">
                <pre><code class="language-javascript">const flip =
    handler => // 接收一个函数作为参数
        (first, ...rest) => // 返回一个新函数，接收参数
            handler(...rest, first); // 调用原函数，但参数顺序翻转</code></pre>
            </div>

            <h3>在表单验证中的应用</h3>
            <p>在表单验证系统中，flip 函数的应用体现在：</p>
            <ol>
                <li><strong>参数顺序调整</strong>：原始验证函数接收 (value, rule) 作为参数，通过 flip 后变成 (rule, value) 顺序</li>
                <li><strong>部分应用</strong>：翻转参数顺序后，我们可以先绑定规则参数，创建专用的验证函数</li>
                <li><strong>函数组合</strong>：这种方式使得验证函数可以更容易地组合和复用</li>
            </ol>

            <h3>业务价值</h3>
            <p>在实际业务中，这种方式带来的好处包括：</p>
            <ul>
                <li>验证规则可以动态配置和组合</li>
                <li>验证逻辑与UI展示分离，便于维护</li>
                <li>可以轻松实现复杂的表单验证需求</li>
                <li>验证规则可以序列化存储，支持动态表单系统</li>
            </ul>
        </div>
    </div>

    <script>
        // flip 函数实现
        const flip =
            handler =>
                (first, ...rest) =>
                    handler(...rest, first);

        // 基础验证函数 - 接收 (value, rule) 作为参数
        const validators = {
            required: (value, rule) => value !== undefined && value !== null && value !== '',
            minLength: (value, rule) => value.length >= rule,
            maxLength: (value, rule) => value.length <= rule,
            email: (value, rule) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
            numeric: (value, rule) => !isNaN(parseFloat(value)) && isFinite(value),
            min: (value, rule) => parseFloat(value) >= rule,
            max: (value, rule) => parseFloat(value) <= rule,
            pattern: (value, rule) => new RegExp(rule).test(value),
            oneOf: (value, rule) => rule.split(',').includes(value)
        };

        // 使用 flip 翻转参数顺序，创建 (rule, value) 顺序的验证函数
        const flippedValidators = {};
        for (const key in validators) {
            flippedValidators[key] = flip(validators[key]);
        }

        // 错误消息生成
        const getErrorMessage = (field, type, param) => {
            const messages = {
                required: `${field} 不能为空`,
                minLength: `${field} 长度不能小于 ${param} 个字符`,
                maxLength: `${field} 长度不能超过 ${param} 个字符`,
                email: `${field} 必须是有效的邮箱格式`,
                numeric: `${field} 必须是数字`,
                min: `${field} 不能小于 ${param}`,
                max: `${field} 不能大于 ${param}`,
                pattern: `${field} 格式不正确`,
                oneOf: `${field} 必须是以下值之一: ${param}`
            };
            return messages[type] || `${field} 验证失败`;
        };

        // 创建字段验证函数 - 先接收规则，再接收值
        const createFieldValidator = (field, type, param) => {
            const validator = flippedValidators[type];
            if (!validator) return null;

            // 创建一个已经绑定了规则参数的验证函数
            const boundValidator = validator.bind(null, param);

            // 返回一个接收字段值的函数
            return (formData) => {
                const result = boundValidator(formData[field]);
                return {
                    field,
                    type,
                    param,
                    value: formData[field],
                    valid: result,
                    message: result ?
                        `${field} 验证通过` :
                        getErrorMessage(field, type, param)
                };
            };
        };

        // 组合多个验证规则
        const validateForm = (validators, formData) => {
            return validators.map(validator => validator(formData));
        };

        // UI 交互逻辑
        document.addEventListener('DOMContentLoaded', () => {
            const validationRules = [];
            const rulesList = document.getElementById('rules-list');
            const validationResults = document.getElementById('validation-results');

            // 添加规则
            document.getElementById('add-rule-btn').addEventListener('click', () => {
                const field = document.getElementById('rule-field').value;
                const type = document.getElementById('rule-type').value;
                const value = document.getElementById('rule-value').value;

                // 创建验证函数
                const validator = createFieldValidator(field, type, value);
                if (validator) {
                    const ruleId = Date.now();
                    validationRules.push({ id: ruleId, validator, field, type, value });
                    updateRulesList();
                }
            });

            // 验证表单
            document.getElementById('validate-btn').addEventListener('click', () => {
                const formData = {
                    username: document.getElementById('username').value,
                    email: document.getElementById('email').value,
                    password: document.getElementById('password').value,
                    age: document.getElementById('age').value,
                    role: document.getElementById('role').value
                };

                const validators = validationRules.map(rule => rule.validator);
                const results = validateForm(validators, formData);

                displayResults(results);
            });

            // 更新规则列表
            function updateRulesList() {
                rulesList.innerHTML = '';

                if (validationRules.length === 0) {
                    rulesList.innerHTML = '<p>暂无验证规则，请添加</p>';
                    return;
                }

                validationRules.forEach(rule => {
                    const ruleItem = document.createElement('div');
                    ruleItem.className = 'rule-item';

                    const ruleText = document.createElement('div');
                    ruleText.textContent = `${rule.field} - ${rule.type}${rule.value ? ': ' + rule.value : ''}`;

                    const deleteBtn = document.createElement('button');
                    deleteBtn.textContent = '删除';
                    deleteBtn.addEventListener('click', () => {
                        const index = validationRules.findIndex(r => r.id === rule.id);
                        if (index !== -1) {
                            validationRules.splice(index, 1);
                            updateRulesList();
                        }
                    });

                    ruleItem.appendChild(ruleText);
                    ruleItem.appendChild(deleteBtn);
                    rulesList.appendChild(ruleItem);
                });
            }

            // 显示验证结果
            function displayResults(results) {
                validationResults.innerHTML = '';

                results.forEach(result => {
                    const resultItem = document.createElement('div');
                    resultItem.className = `result-item ${result.valid ? 'success' : 'error'}`;
                    resultItem.textContent = result.message;
                    validationResults.appendChild(resultItem);
                });
            }

            // 初始化规则列表
            updateRulesList();

            // 标签切换
            const tabs = document.querySelectorAll('.tab');
            tabs.forEach(tab => {
                tab.addEventListener('click', () => {
                    // 移除所有活动标签
                    tabs.forEach(t => t.classList.remove('active'));
                    document.querySelectorAll('.tab-content').forEach(content => {
                        content.classList.remove('active');
                    });

                    // 激活当前标签
                    tab.classList.add('active');
                    const tabId = tab.getAttribute('data-tab');
                    document.getElementById(tabId).classList.add('active');
                });
            });

            // 添加一些默认规则
            setTimeout(() => {
                const defaultRules = [
                    { field: 'username', type: 'required', value: '' },
                    { field: 'username', type: 'minLength', value: '3' },
                    { field: 'email', type: 'email', value: '' },
                    { field: 'password', type: 'minLength', value: '6' },
                    { field: 'age', type: 'numeric', value: '' },
                    { field: 'age', type: 'min', value: '18' },
                    { field: 'role', type: 'oneOf', value: 'admin,user,guest' }
                ];

                defaultRules.forEach(rule => {
                    const validator = createFieldValidator(rule.field, rule.type, rule.value);
                    if (validator) {
                        const ruleId = Date.now() + Math.random();
                        validationRules.push({
                            id: ruleId,
                            validator,
                            field: rule.field,
                            type: rule.type,
                            value: rule.value
                        });
                    }
                });

                updateRulesList();
            }, 100);
        });
    </script>
    <!-- 添加highlight.js的JS -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
    <script>
        // 初始化代码高亮
        document.addEventListener('DOMContentLoaded', () => {
            document.querySelectorAll('pre code').forEach((block) => {
                hljs.highlightElement(block);
            });

            // 标签切换时重新应用高亮
            document.querySelectorAll('.tab').forEach(tab => {
                tab.addEventListener('click', () => {
                    setTimeout(() => {
                        document.querySelectorAll('pre code').forEach((block) => {
                            hljs.highlightElement(block);
                        });
                    }, 10);
                });
            });
        });
    </script>
</body>

</html>